menu binding: emit submenu close after activate
authorRyan Lortie <desrt@desrt.ca>
Mon, 16 Jun 2014 18:26:21 +0000 (14:26 -0400)
committerRyan Lortie <desrt@desrt.ca>
Mon, 16 Jun 2014 19:34:42 +0000 (15:34 -0400)
We want to make sure that the submenu action is changed back to FALSE
_after_ the menu item has been activated.  This prevents the menu
teardown handler from deleting the menu item before it can be activated.

Unfortunately, GtkMenuShell emits "hide" before the item activation.
This is probably done to prevent the application from doing things like
showing dialogs when the menu is still holding the grab.

In the case where we are doing an activate, set a boolean flag on each
of the open menus (following the parent stack) indicating that we'll be
emitting another signal soon (selection done).  If that flag is set, we
defer the setting of the submenu action until we receive the second
signal.

https://bugzilla.gnome.org/show_bug.cgi?id=729820

gtk/gtkmenushell.c
gtk/gtkmenushellprivate.h

index 18f154239ee86076768df803cff167eabe67776a..d1d0c7f07b321df2f9717b8cc04f7fb4bf5258d6 100644 (file)
@@ -1360,6 +1360,8 @@ gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
 
       do
         {
+          parent_menu_shell->priv->selection_done_coming_soon = TRUE;
+
           g_object_ref (parent_menu_shell);
           shells = g_slist_prepend (shells, parent_menu_shell);
           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->priv->parent_menu_shell;
@@ -1379,7 +1381,10 @@ gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
 
   for (slist = shells; slist; slist = slist->next)
     {
-      g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
+      GtkMenuShell *parent_menu_shell = slist->data;
+
+      g_signal_emit (parent_menu_shell, menu_shell_signals[SELECTION_DONE], 0);
+      parent_menu_shell->priv->selection_done_coming_soon = FALSE;
       g_object_unref (slist->data);
     }
   g_slist_free (shells);
@@ -2010,7 +2015,18 @@ gtk_menu_shell_submenu_hidden (GtkWidget *submenu,
 {
   GtkMenuTrackerItem *item = user_data;
 
-  gtk_menu_tracker_item_request_submenu_shown (item, FALSE);
+  if (!GTK_MENU_SHELL (submenu)->priv->selection_done_coming_soon)
+    gtk_menu_tracker_item_request_submenu_shown (item, FALSE);
+}
+
+static void
+gtk_menu_shell_submenu_selection_done (GtkWidget *submenu,
+                                       gpointer   user_data)
+{
+  GtkMenuTrackerItem *item = user_data;
+
+  if (GTK_MENU_SHELL (submenu)->priv->selection_done_coming_soon)
+    gtk_menu_tracker_item_request_submenu_shown (item, FALSE);
 }
 
 static void
@@ -2091,6 +2107,7 @@ gtk_menu_shell_tracker_insert_func (GtkMenuTrackerItem *item,
            */
           g_signal_connect (submenu, "show", G_CALLBACK (gtk_menu_shell_submenu_shown), item);
           g_signal_connect (submenu, "hide", G_CALLBACK (gtk_menu_shell_submenu_hidden), item);
+          g_signal_connect (submenu, "selection-done", G_CALLBACK (gtk_menu_shell_submenu_selection_done), item);
         }
 
       gtk_widget_show (widget);
index 622f0fd3e72b903ed4eefa57d08319ad34ec58a0..233be343afd68d1659bf09cfbd43e20c395bc932 100644 (file)
@@ -61,6 +61,11 @@ struct _GtkMenuShellPrivate
                                    * the user moves the mouse over
                                    * an unselectable menuitem.
                                    */
+
+  guint selection_done_coming_soon : 1; /* Set TRUE when a selection-done
+                                         * signal is coming soon (when checked
+                                         * from inside of a "hide" handler).
+                                         */
   GtkMnemonicHash *mnemonic_hash;
   GtkKeyHash *key_hash;